package org.ns.framework.datasource.config;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.ns.basics.commons.lang.StringUtils;

/**
 * mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database.collection][?options]]
 * 
 * @author zhangxingyu
 *
 */
public class MongoConfig {

	@Target(ElementType.FIELD)
	@Retention(RetentionPolicy.RUNTIME)
	public static @interface MongoNotParam {

	}

	private static class ConfigFields {
		public static final List<Field> paramFields = new ArrayList<Field>();
		static {
			Field[] fields = MongoConfig.class.getDeclaredFields();
			for (Field field : fields) {
				int mod = field.getModifiers();
				boolean use = !Modifier.isFinal(mod) && !Modifier.isStatic(mod);
				if (use && !field.isAnnotationPresent(MongoNotParam.class)) {
					field.setAccessible(true);
					paramFields.add(field);
				}
			}
		}
	}

	private static Map<String, Object> getParamValues(MongoConfig config) {
		Map<String, Object> map = new HashMap<>();
		for (Field field : ConfigFields.paramFields) {
			try {
				Object value = field.get(config);
				if (value != null) {
					map.put(field.getName(), value);
				}
			} catch (Exception e) {
			}
		}
		return map;
	}

	/**
	 * 连接String mongodb://xxxxx/
	 */
	@MongoNotParam
	private String protocol = "mongodb://";
	@MongoNotParam
	private String hosts = "127.0.0.1:27017"; // host1:port1,host2:port2
	@MongoNotParam
	private String username;
	@MongoNotParam
	private String password;

	private Integer minPoolSize;
	private Integer maxPoolSize;
	private Integer maxIdleTimems;
	private Integer maxLifeTimems;
	private Integer waitQueueMultiple;
	private Integer waitQueueTimeoutMs;
	private Integer connectTimeoutMs;
	private Integer socketTimeoutMs;
	private Boolean tlsAllowInvalidHostnames;
	private Boolean sslInvalidHostnameAllowed;
	private Boolean tlsInsecure;
	private String ssl;
	private String tls;
	private String streamType;
	private String replicaSet;
	private String readConcernLevel;
	private Integer serverSelectionTimeoutMs;
	private Integer localThresholdMs;
	private Integer heartbeatFrequencyMs;
	private String appName;
	private Boolean retryWrites;
	private Boolean retryReads;

	public MongoConfig() {

	}

	public String getProtocol() {
		return protocol;
	}

	public void setProtocol(String protocol) {
		this.protocol = protocol;
	}

	public String getHosts() {
		return hosts;
	}

	public void setHosts(String hosts) {
		this.hosts = hosts;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public Integer getMinPoolSize() {
		return minPoolSize;
	}

	public void setMinPoolSize(Integer minPoolSize) {
		this.minPoolSize = minPoolSize;
	}

	public Integer getMaxPoolSize() {
		return maxPoolSize;
	}

	public void setMaxPoolSize(Integer maxPoolSize) {
		this.maxPoolSize = maxPoolSize;
	}

	public Integer getMaxIdleTimems() {
		return maxIdleTimems;
	}

	public void setMaxIdleTimems(Integer maxIdleTimems) {
		this.maxIdleTimems = maxIdleTimems;
	}

	public Integer getMaxLifeTimems() {
		return maxLifeTimems;
	}

	public void setMaxLifeTimems(Integer maxLifeTimems) {
		this.maxLifeTimems = maxLifeTimems;
	}

	public Integer getWaitQueueMultiple() {
		return waitQueueMultiple;
	}

	public void setWaitQueueMultiple(Integer waitQueueMultiple) {
		this.waitQueueMultiple = waitQueueMultiple;
	}

	public Integer getWaitQueueTimeoutMs() {
		return waitQueueTimeoutMs;
	}

	public void setWaitQueueTimeoutMs(Integer waitQueueTimeoutMs) {
		this.waitQueueTimeoutMs = waitQueueTimeoutMs;
	}

	public Integer getConnectTimeoutMs() {
		return connectTimeoutMs;
	}

	public void setConnectTimeoutMs(Integer connectTimeoutMs) {
		this.connectTimeoutMs = connectTimeoutMs;
	}

	public Integer getSocketTimeoutMs() {
		return socketTimeoutMs;
	}

	public void setSocketTimeoutMs(Integer socketTimeoutMs) {
		this.socketTimeoutMs = socketTimeoutMs;
	}

	public Boolean getTlsAllowInvalidHostnames() {
		return tlsAllowInvalidHostnames;
	}

	public void setTlsAllowInvalidHostnames(Boolean tlsAllowInvalidHostnames) {
		this.tlsAllowInvalidHostnames = tlsAllowInvalidHostnames;
	}

	public Boolean getSslInvalidHostnameAllowed() {
		return sslInvalidHostnameAllowed;
	}

	public void setSslInvalidHostnameAllowed(Boolean sslInvalidHostnameAllowed) {
		this.sslInvalidHostnameAllowed = sslInvalidHostnameAllowed;
	}

	public Boolean getTlsInsecure() {
		return tlsInsecure;
	}

	public void setTlsInsecure(Boolean tlsInsecure) {
		this.tlsInsecure = tlsInsecure;
	}

	public String getSsl() {
		return ssl;
	}

	public void setSsl(String ssl) {
		this.ssl = ssl;
	}

	public String getTls() {
		return tls;
	}

	public void setTls(String tls) {
		this.tls = tls;
	}

	public String getStreamType() {
		return streamType;
	}

	public void setStreamType(String streamType) {
		this.streamType = streamType;
	}

	public String getReplicaSet() {
		return replicaSet;
	}

	public void setReplicaSet(String replicaSet) {
		this.replicaSet = replicaSet;
	}

	public String getReadConcernLevel() {
		return readConcernLevel;
	}

	public void setReadConcernLevel(String readConcernLevel) {
		this.readConcernLevel = readConcernLevel;
	}

	public Integer getServerSelectionTimeoutMs() {
		return serverSelectionTimeoutMs;
	}

	public void setServerSelectionTimeoutMs(Integer serverSelectionTimeoutMs) {
		this.serverSelectionTimeoutMs = serverSelectionTimeoutMs;
	}

	public Integer getLocalThresholdMs() {
		return localThresholdMs;
	}

	public void setLocalThresholdMs(Integer localThresholdMs) {
		this.localThresholdMs = localThresholdMs;
	}

	public Integer getHeartbeatFrequencyMs() {
		return heartbeatFrequencyMs;
	}

	public void setHeartbeatFrequencyMs(Integer heartbeatFrequencyMs) {
		this.heartbeatFrequencyMs = heartbeatFrequencyMs;
	}

	public String getAppName() {
		return appName;
	}

	public void setAppName(String appName) {
		this.appName = appName;
	}

	public Boolean getRetryWrites() {
		return retryWrites;
	}

	public void setRetryWrites(Boolean retryWrites) {
		this.retryWrites = retryWrites;
	}

	public Boolean getRetryReads() {
		return retryReads;
	}

	public void setRetryReads(Boolean retryReads) {
		this.retryReads = retryReads;
	}

	@Override
	public String toString() {
		StringBuilder connBuffer = new StringBuilder(256);
		connBuffer.append(this.protocol);
		if (StringUtils.isNotBlank(this.username) && StringUtils.isNotBlank(this.password)) {
			connBuffer.append(this.username).append(":").append(this.password).append("@");
		}
		connBuffer.append(hosts);
		connBuffer.append("/");
		Map<String, Object> params = getParamValues(this);
		if (params.size() > 0) {
			connBuffer.append("?");
			for (Entry<String, Object> entry : params.entrySet()) {
				connBuffer.append(entry.getKey().toLowerCase()).append("=").append(entry.getValue()).append("&");
			}
			connBuffer.setLength(connBuffer.length() - 1);
		}
		String connString = connBuffer.toString();
		return connString;
	}

	public static void main(String[] args) {
		MongoConfig config = new MongoConfig();
		config.setUsername("asas");
		config.setPassword("123456");
		config.setMaxIdleTimems(1234);
		System.out.println(config.toString());

	}
}
